home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 25 / Cream of the Crop 25.iso / faq / wdj0597.zip / SDKANN.ZIP / SOURCE.ZIP / PLAYBACK.C < prev    next >
C/C++ Source or Header  |  1995-08-10  |  12KB  |  407 lines

  1. #include "playback.h"
  2. #include <ctype.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5.  
  6. /****  WDJ_Pbh_Event
  7. This structure describes an individual journal playback hook event (a
  8. mouse or keyboard message).  The functions here create, manipulate, and
  9. playback linked lists of such events (the linked list is not visible via
  10. the API). 
  11. ****/
  12. typedef struct WDJ_Pbh_Event
  13.     {
  14.     struct WDJ_Pbh_Event  *Next;
  15.     UINT        Message;
  16.     UINT        Low;
  17.     UINT        High;
  18.     DWORD       Delay;
  19.     }   WDJ_Pbh_Event;
  20.  
  21. /****  WDJ_Pbh_List
  22. This structure is the head and tail pointer for a linked list of journal
  23. playback hook events.  An opaque pointer to this structure is returned
  24. and accepted by the API. 
  25. ****/
  26. typedef struct WDJ_Pbh_List
  27.     {
  28.     WDJ_Pbh_Event  *First;
  29.     WDJ_Pbh_Event  *Last;
  30.     HINSTANCE       Instance;
  31.     }   WDJ_Pbh_List;
  32.  
  33. #define NEW(type)   (type *)malloc(sizeof(type))
  34.  
  35. static  WDJ_Pbh_List   *InProgress;
  36. static  WDJ_Pbh_Event  *NextEvent;
  37.  
  38. static  WNDPROC         NotifyFunc;
  39. static  HWND            NotifyWindow;
  40. static  UINT            NotifyMessage;
  41. static  WPARAM          NotifyParam1;
  42. static  LPARAM          NotifyParam2;
  43.  
  44. static  EVENTMSG        Event;
  45. static  DWORD           EventDelay;
  46. static  DWORD           EventDelayStart;
  47.  
  48. static  HHOOK           OldHook;
  49.  
  50. /* forward declare the lowest-level playback function */
  51. static int StartPlayback(WDJ_Pbh Handle, WNDPROC Callback,
  52.                          HWND W, UINT M, WPARAM p1, LPARAM p2);
  53.  
  54. /* and the hook function itself */
  55. LRESULT CALLBACK _export PlayBackHook(int Code,
  56.                                WPARAM Param1, LPARAM Param2);
  57.  
  58.  
  59. static UINT    KeyId(UINT VirtualKey)
  60.     {
  61.     return (VirtualKey&0x00FF) | (MapVirtualKey(VirtualKey, 0)<<8);
  62.     }
  63.  
  64.  
  65. /* Create - Create the initial linked-list of events */
  66. WDJ_Pbh WDJ_Pbh_Create(HINSTANCE Instance)
  67.     {
  68.     /* Allocate new list */
  69.     WDJ_Pbh_List   *List = NEW(WDJ_Pbh_List);
  70.  
  71.     /* if we got the memory */
  72.     if(List)
  73.         {
  74.         /* initialize ptrs to NULL */
  75.         List->First     = 0;
  76.         List->Last      = 0;
  77.         List->Instance  = Instance;
  78.         }
  79.  
  80.     /* return handle, or NULL if failed */
  81.     return (WDJ_Pbh)List;
  82.     }
  83.  
  84. /* AppendRaw - lowest-level function to add event to list */
  85. int  WDJ_Pbh_AppendRaw(WDJ_Pbh Handle, UINT Message, UINT Low,
  86.                 UINT High, DWORD Delay)
  87.     {
  88.     /* turn handle into event list */
  89.     WDJ_Pbh_List  *List  = (WDJ_Pbh_List *)Handle;
  90.  
  91.     /* if we were handed a non-null ptr */
  92.     if(List)
  93.         {
  94.         /* allocate a new event record */
  95.         WDJ_Pbh_Event *Event = NEW(WDJ_Pbh_Event);
  96.  
  97.         /* if we got the memory */
  98.         if(Event)
  99.             {
  100.             /* Next is NULL, since we are last in linked list */
  101.             Event->Next     = 0;
  102.             /* raw copy -- caller better know what he's doing */
  103.             Event->Message  = Message;
  104.             Event->Low      = Low;
  105.             Event->High     = High;
  106. //??????
  107.             Event->Delay    = 50;
  108. //            Event->Delay    = Delay;
  109.  
  110.             /* link new event into list */
  111.             if(!List->First)
  112.                 List->First = List->Last    = Event;
  113.             else
  114.                 {
  115.                 List->Last->Next    = Event;
  116.                 List->Last          = Event;
  117.                 }
  118.             return TRUE;
  119.             }
  120.         }
  121.  
  122.     /* couldn't get memory for new event */
  123.     return FALSE;
  124.     }
  125.  
  126. /* AppendChar - generate up-down strokes for a character */
  127. int WDJ_Pbh_AppendChar(WDJ_Pbh Handle, int Char)
  128.     {
  129.     /* turn handle into event list */
  130.     WDJ_Pbh_List  *List  = (WDJ_Pbh_List *)Handle;
  131.  
  132.     /* if we were handed a non-null ptr */
  133.     if(List)
  134.         {
  135.         UINT    Vchar = VkKeyScan(Char&0x00FF);
  136.         int     Shift = (Vchar&0x0100) != 0;
  137.         int Sys = 0;    /* assume it will not be sys key */
  138.         if((Char&WDJ_PBH_ALT) && !(Char&WDJ_PBH_CTL))
  139.             Sys += 4;   /* +4 turns WM_KEY?? into WM_SYSKEY?? */
  140.  
  141.         if(Char & WDJ_PBH_ALT)
  142.             WDJ_Pbh_AppendRaw(Handle, WM_SYSKEYDOWN, KeyId(VK_MENU), 1, 0);
  143.         if(Char & WDJ_PBH_CTL)
  144.             WDJ_Pbh_AppendRaw(Handle, WM_KEYDOWN, KeyId(VK_CONTROL), 1, 0);
  145.         if(Shift)
  146.             WDJ_Pbh_AppendRaw(Handle, WM_KEYDOWN, KeyId(VK_SHIFT), 1, 0);
  147.  
  148.         WDJ_Pbh_AppendRaw(Handle, WM_KEYDOWN+Sys, KeyId(Vchar), 1, 0);
  149.         WDJ_Pbh_AppendRaw(Handle, WM_KEYUP+Sys,   KeyId(Vchar), 1, 0);
  150.  
  151.         if(Shift)
  152.             WDJ_Pbh_AppendRaw(Handle, WM_KEYUP, KeyId(VK_SHIFT), 1, 0);
  153.         if(Char & WDJ_PBH_CTL)
  154.             WDJ_Pbh_AppendRaw(Handle, WM_KEYUP, KeyId(VK_CONTROL), 1, 0);
  155.         if(Char & WDJ_PBH_ALT)
  156.             WDJ_Pbh_AppendRaw(Handle, WM_KEYUP, KeyId(VK_MENU), 1, 0);
  157.  
  158.         return TRUE;
  159.         }
  160.     return FALSE;
  161.     }
  162.  
  163. /* AppendString - do multiple chars */
  164. int WDJ_Pbh_AppendString(WDJ_Pbh Handle, const char *String)
  165.     {
  166.     /* turn handle into event list */
  167.     WDJ_Pbh_List  *List  = (WDJ_Pbh_List *)Handle;
  168.  
  169.     /* if we were handed a non-null ptr */
  170.     if(List && String)
  171.         {
  172.         while(*String)
  173.             WDJ_Pbh_AppendChar(Handle, *String++);
  174.         }
  175.     return FALSE;
  176.     }
  177.  
  178. /* PlayCallback - Playback events, then call callback function */
  179. int WDJ_Pbh_PlayCallback(WDJ_Pbh Handle, WNDPROC Callback,
  180.                      HWND W, UINT M, WPARAM p1, LPARAM p2)
  181.     {
  182.     return StartPlayback(Handle, Callback, W, M, p1, p2);
  183.     }
  184.  
  185. /* PumpCallback - Playback events, then call do message pump */
  186. int WDJ_Pbh_PumpCallback(WDJ_Pbh Handle, WNDPROC Callback,
  187.                      HWND W, UINT M, WPARAM p1, LPARAM p2)
  188.     {
  189.     MSG     Message;
  190.     int Result;
  191.  
  192.     Result  = StartPlayback(Handle, Callback, W, M, p1, p2);
  193.     if(Result == FALSE)
  194.         return FALSE;
  195.  
  196.     /*  pump messages until none left. Presumably all
  197.      *  events will be played back by then. Note: this
  198.      *  works perfectly only if the app being pumped
  199.      *  was using a loop just like this one.
  200.      */
  201.     while(InProgress)
  202.     while(PeekMessage(&Message, NULL, 0, 0, PM_REMOVE))
  203.         if(Message.message == WM_QUIT)
  204.             {
  205.             PostQuitMessage(0);
  206.             return TRUE;
  207.             }
  208.         else
  209.             {
  210.             TranslateMessage(&Message);
  211.             DispatchMessage(&Message);
  212.             }
  213.  
  214.     return TRUE;
  215.     }
  216.  
  217. int WDJ_Pbh_String(HINSTANCE Instance, const char *String)
  218.     {
  219.     int     Char;
  220.     int     Alt;
  221.     WDJ_Pbh Handle;
  222.  
  223.     Handle = WDJ_Pbh_Create(Instance);
  224.     if(!Handle || !String)
  225.         return FALSE;
  226.     while((Char = *String++) != '\0')
  227.         {
  228.         Alt = FALSE;
  229.         if(Char == '^')
  230.             {
  231.             Char    = *String++;
  232.             if(Char && Char != '^')
  233.                 Alt = TRUE;
  234.             }
  235.         if(Char)
  236.             WDJ_Pbh_AppendChar(Handle, Char | (Alt?WDJ_PBH_ALT:0));
  237.         }
  238.     WDJ_Pbh_Pump(Handle);
  239.     WDJ_Pbh_Destroy(Handle);
  240.     return TRUE;
  241.     }
  242.  
  243. /* StartPlayback - Start hook, remember what func to call at end */
  244. static int StartPlayback(WDJ_Pbh Handle, WNDPROC Callback,
  245.                          HWND W, UINT M, WPARAM p1, LPARAM p2)
  246.     {
  247. if(InProgress)
  248.     OutputDebugString("Playback already in progress!\n");
  249.     if(InProgress || Handle == NULL)
  250.         return FALSE;
  251.     else
  252.         {
  253.         InProgress  = (WDJ_Pbh_List *)Handle;
  254.         if(InProgress == NULL || InProgress->First == NULL)
  255.             return FALSE;
  256.         NextEvent   = InProgress->First;
  257.         }
  258.  
  259.     NotifyFunc      = Callback;
  260.     NotifyWindow    = W;
  261.     NotifyMessage   = M;
  262.     NotifyParam1    = p1;
  263.     NotifyParam2    = p2;
  264.         
  265. //??? get initial state of up/down keys here!
  266.  
  267.     OldHook     = SetWindowsHookEx(WH_JOURNALPLAYBACK,
  268.                               PlayBackHook, InProgress->Instance, NULL);
  269.     return OldHook != NULL;
  270.     }
  271.  
  272. /* Destroy - free list and recover memory */
  273. int WDJ_Pbh_Destroy(WDJ_Pbh Handle)
  274.     {
  275.     WDJ_Pbh_List  *List  = (WDJ_Pbh_List *)Handle;
  276.  
  277.     /* if looks like valid handle */
  278.     if(List)
  279.         {
  280.         WDJ_Pbh_Event *Event, *Next;
  281.         Event = List->First;
  282.         while(Event)
  283.             {
  284.             Next    = Event->Next;
  285.             free(Event);
  286.             Event   = Next;
  287.             }
  288.         return TRUE;
  289.         }
  290.     return FALSE;
  291.     }
  292.  
  293. int     WDJ_Pbh_Play(WDJ_Pbh Handle)
  294.     {
  295.     return WDJ_Pbh_PlayCallback(Handle, 0, 0, 0, 0, 0);
  296.     }
  297.  
  298. int     WDJ_Pbh_Pump(WDJ_Pbh Handle)
  299.     {
  300.     return WDJ_Pbh_PumpCallback(Handle, 0, 0, 0, 0, 0);
  301.     }
  302.  
  303.  
  304. static void TerminatePlayback(void)
  305.     {
  306. OutputDebugString("TerminatePlayback\n");
  307.     UnhookWindowsHookEx(OldHook);
  308. OutputDebugString("unhooked\n");
  309. OutputDebugString("freeed\n");
  310.     
  311.     Event.message   = 0;
  312.     InProgress      = 0;
  313.     if(NotifyFunc)
  314.         NotifyFunc(NotifyWindow, NotifyMessage, NotifyParam1, NotifyParam2);
  315. OutputDebugString("notified\n");
  316.     }
  317.  
  318. static int GetNextEvent(void)
  319.     {
  320.     /* if input queue is empty */
  321.     if(!NextEvent)
  322.         return FALSE;
  323.     Event.message   = NextEvent->Message;
  324.     Event.paramL    = NextEvent->Low;
  325.     Event.paramH    = NextEvent->High;
  326.     Event.time      = GetTickCount();
  327.     EventDelay      = NextEvent->Delay;
  328.     EventDelayStart = Event.time;
  329.  
  330.     NextEvent       = NextEvent->Next;
  331.     return TRUE;
  332.     }
  333.  
  334. const char *Grok(UINT Msg)
  335.     {
  336.     static char Buffer[128];
  337.     switch(Msg)
  338.         {
  339.         case    WM_SYSKEYDOWN   : return "WM_SYSKEYDOWN";
  340.         case    WM_KEYDOWN      : return "WM_KEYDOWN";
  341.         case    WM_SYSKEYUP     : return "WM_SYSKEYUP";
  342.         case    WM_KEYUP        : return "WM_KEYUP";
  343.         default:
  344.             wsprintf(Buffer, "0x%04X (%d)", Msg, Msg);
  345.             return Buffer;
  346.         }
  347.     }
  348.  
  349. LRESULT CALLBACK _export PlayBackHook(int Code,
  350.                                WPARAM Param1, LPARAM Param2)
  351.     {
  352.     long    Result          = 0;
  353.     int     CallNextHook    = FALSE;
  354.     switch(Code)
  355.         {
  356.         case    HC_GETNEXT      :
  357.             {
  358. char Buffer[256];
  359.             /* first code after installing hook is HC_GETNEXT,
  360.              * hence, we use Event.message as a flag that says
  361.              * we need to prime the pump. This way, if Windows
  362.              * changes someday to issue HC_SKIP first, the code
  363.              * here still works.
  364.              */
  365.             if(Event.message == 0)
  366.                 GetNextEvent();
  367. //???
  368. //            Event.time  = GetTickCount();
  369. #if 0
  370. wsprintf(Buffer, "HC_GETNEXT m='%s', l=%d, h=%d, time=%ld D=%ld\n",
  371.     Grok(Event.message), Event.paramL, Event.paramH,
  372.     Event.time, EventDelay);
  373.  
  374. OutputDebugString( Buffer );
  375. #endif
  376.             *(EVENTMSG*)Param2  = Event;
  377. //???            return EventDelay;
  378.             {
  379.             DWORD   Elapsed = GetTickCount() - EventDelayStart;
  380.             if(Elapsed >= EventDelay)
  381.                 return 0;
  382.             else
  383.                 return EventDelay - Elapsed;
  384.             }
  385.             }
  386.         case    HC_SKIP         :
  387. //OutputDebugString( "HC_SKIP\n");
  388.             if(!GetNextEvent())
  389.                 TerminatePlayback();
  390.             break;
  391.         case    HC_SYSMODALOFF  :
  392. OutputDebugString( "HC_SYSMODALOFF\n");
  393.                 TerminatePlayback();
  394.         case    HC_SYSMODALON   :
  395.         default :
  396. OutputDebugString( "HC_SYSMODALON, default\n");
  397.             CallNextHook    = TRUE;
  398.         }
  399.     if(CallNextHook)
  400. {
  401. OutputDebugString( "Call that next hook!\n" );
  402.         Result  = CallNextHookEx(OldHook, Code, Param1, Param2);
  403. }
  404.     return Result;
  405.     }
  406.  
  407.